import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.IFill;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Property;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.sql.Types;
import java.util.*;

/**
 * Hello world!
 */
public class App {

    public static void main(String[] args) {

        String projectDir =  Objects.requireNonNull(App.class.getResource("")).getPath().replaceAll("^/","").replaceAll("/target/.*?$","");

        YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();
        yamlProFb.setResources(new ClassPathResource("application.yml"));
        Properties props = yamlProFb.getObject();
        if(props == null) {
            throw new RuntimeException("配置信息不存在");
        }
        //数据库路径
        String url = props.getProperty("mybatis.mysql.jdbcUrl").trim();
        //数据库用户密码
        String username = props.getProperty("mybatis.mysql.username").trim();
        //数据库用户密码
        String password = props.getProperty("mybatis.mysql.password").trim();
        //entity包路径
        String entity = props.getOrDefault("mybatis.package.entity","entity").toString().trim();
        //包名
        String packageName = props.getProperty("mybatis.package.name").trim();
        //String projectDir = props.getProperty("mybatis.projectDir").trim().replaceAll("\\$", "");
        //忽略前缀
        String ignorePrefix = props.getProperty("mybatis.table.ignorePrefix");
        Set<String> ignorePrefixList = new HashSet<>();
        ignorePrefixList.add("t_");
        ignorePrefixList.add("c_");
        if(ignorePrefix !=null && !ignorePrefix.isEmpty()) {
            for (String iPrefix : ignorePrefix.trim().split(",")) {
                if(iPrefix != null && iPrefix.isEmpty()) {
                    ignorePrefixList.add(iPrefix.trim());
                }
            }
            ignorePrefixList.addAll(Arrays.asList(ignorePrefix.trim().split(",")));
        }
        //表名
        String tableName = props.getProperty("mybatis.table.name").trim();
        Set<String> tableNameList = new HashSet<>();
        for (String tName : tableName.split(",")) {
            if(tName != null && !tName.isEmpty()) {
                tableNameList.add(tName.trim());
            }
        }
        if(tableNameList.isEmpty()) {
            throw new RuntimeException("表名不能为空");
        }
        FastAutoGenerator.create(url, username, password)
                .globalConfig(builder ->
                    builder.author("FastAutoGenerator") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .disableOpenDir() //禁止打开输出目录
                            .outputDir(projectDir + "\\src\\main\\java") // 指定输出目录
                )
                .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    return typeRegistry.getColumnType(metaInfo);

                }))
                .packageConfig(builder ->
                    builder.parent(packageName) // 设置父包名
                            //.moduleName("system") // 设置父包模块名
                            .entity(entity)
                            .controller("controller")
                            .service("service")
                            .serviceImpl("service.impl")
                            .mapper("idal.mapper")
                            .xml("xml")
                            //.pathInfo(Collections.singletonMap(OutputFile.xml,projectDir + "\\src\\main\\java\\" + packageName.replaceAll("\\.", "\\\\") +"\\xml")) // 设置mapperXml生成路径
                )
                .strategyConfig(builder ->
                    builder.addInclude(tableNameList.toArray(new String[]{})) // 设置需要生成的表名
                            .addTablePrefix(ignorePrefixList.toArray(new String[]{})) // 设置过滤表前缀
                            .controllerBuilder()
                            .enableRestStyle()
                            .enableFileOverride()
                            .build()
                            .entityBuilder()
                            .disableSerialVersionUID()
                            .formatFileName("%sEntity")
                            .enableLombok()
                            .idType(IdType.ASSIGN_ID)
                            .enableFileOverride()
                            .addTableFills(fills())
                            .build()
                            .mapperBuilder()
                            .enableFileOverride()
                            .build()
                            .serviceBuilder()
                            .enableFileOverride()
                            .build()
                )
                .injectionConfig(consumer -> {
                    List<CustomFile> customFiles = new ArrayList<>();
                    // VO
                    CustomFile.Builder builder1 = new CustomFile.Builder();
                    builder1.fileName("VO.java");
                    builder1.packageName(entity + ".vo");
                    builder1.templatePath("/templates/entityVO.java.ftl");
                    builder1.enableFileOverride();
                    customFiles.add(builder1.build());

                    consumer.customFile(customFiles);
                    consumer.beforeOutputFile((t, meta)-> {
                        meta.put("entityVO", t.getEntityName().replaceAll("Entity$", "") + "VO");
                        meta.put("packageVO", "vo");
                    });
                })
                .templateEngine(new MyFreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();
    }

    public static List<IFill> fills() {
        List<IFill> list = new ArrayList<>();
        list.add(new Property("creator", FieldFill.INSERT));
        list.add(new Property("createUserId", FieldFill.INSERT));
        list.add(new Property("createdName", FieldFill.INSERT));
        list.add(new Property("isEnable", FieldFill.INSERT));
        list.add(new Property("tenantId", FieldFill.INSERT));
        list.add(new Property("createdDate", FieldFill.INSERT));
        list.add(new Property("createTime", FieldFill.INSERT));
        list.add(new Property("updateControlId", FieldFill.INSERT_UPDATE));
        list.add(new Property("lastUpdatedDate", FieldFill.INSERT_UPDATE));
        list.add(new Property("modifier", FieldFill.INSERT_UPDATE));
        return list;
    }

    static class MyFreemarkerTemplateEngine extends FreemarkerTemplateEngine {

        @Override
        protected void outputCustomFile(List<CustomFile> customFiles, TableInfo tableInfo, Map<String, Object> objectMap) {
            String entityName = tableInfo.getEntityName();
            String parentPath = getPathInfo(OutputFile.parent);
            customFiles.forEach(file -> {
                String filePath = StringUtils.isNotBlank(file.getFilePath()) ? file.getFilePath() : parentPath;
                if (StringUtils.isNotBlank(file.getPackageName())) {
                    filePath = filePath + File.separator + file.getPackageName();
                    filePath = filePath.replaceAll("\\.", StringPool.BACK_SLASH + File.separator);
                }
                String fileName = filePath + File.separator + entityName.replaceAll("Entity$", "")  + file.getFileName();

                outputFile(new File(fileName), objectMap, file.getTemplatePath(), file.isFileOverride());
            });
        }
    }
}
